package com.lb.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.lb.anno.MysqlAutoPK;
import com.lb.exception.PojoStructureException;
import org.apache.commons.beanutils.PropertyUtils;

/**
 * @author user
 */
public class MyBatisReflectUtil {
    /**
     * 用于存放POJO的列信息
     */
    private transient static Map<String, List<String>> columnMap = new HashMap<String, List<String>>();
    /*
     * 为注解字段的数据
     */
    private transient static Map<String, Class<?>> columnAttrAnnotationMap = new HashMap();
    /**
     * 当前对象实体
     */
    private transient static Object o;

    private Object getObject() {
        return o;
    }

    /**
     * 获取POJO对应的表名 需要POJO中的属性定义@Table(name)
     *
     * @return
     */
    public static String tablename(Object o) {
        Table table = o.getClass().getAnnotation(Table.class);
        if (table != null) {
            return table.name();
        } else {
            throw new PojoStructureException("undefine POJO @Table, need Tablename(@Table(name))");
        }
    }

    public static String tablename(Class clazz) {
        Table table = (Table) clazz.getAnnotation(Table.class);
        if (table != null) {
            return table.name();
        } else {
            throw new PojoStructureException("undefine POJO @Table, need Tablename(@Table(name))");
        }
    }

    public MyBatisReflectUtil() {
    }

    /**
     * 获取POJO中的主键字段名 需要定义@Id
     *
     * @return
     */
    public static Field idNew(Object o) {
        Map map = getAllAttrField(o);
        if (map.get("objIdmysql") != null) {
            Field f= (Field) map.get("objIdmysql");
            return f;
        } else if (map.get("objId") != null) {
            Field f= (Field) map.get("objId");
            return f;
        }
        throw new RuntimeException("undefine POJO @Id");
    }

    /**
     * 判断实体属性值是否为null
     * @param o
     * @param fieldname
     * @return
     */
    private boolean isNull(Object o, String fieldname) {
        try {
            Map map = getAllAttrField(o);
            List<Field> fields = null;
            Field field = null;
            if (map.get("fields") != null) {
                fields = (List) map.get("fields");
                for (Field fd : fields) {
                    if (fd.getName() != null && fd.getName().equals(fieldname)) {
                        field = fd;
                        break;
                    }
                }
            }
            if (field == null) {
                return true;
            } else {
                return PropertyUtils.getProperty(o, field.getName()) == null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 初始实体的属性字段，用于计算类定义 需要POJO中的属性定义@Column(name)
     */
    public void caculationColumnList(Object o) {
        MyBatisReflectUtil.o = o;
        if (columnMap.containsKey(o.getClass().getSimpleName())) {
            return;
        }
        List<Field> allFields = new ArrayList<>();
        List<String> columnAttrList = new ArrayList<String>();
        List<String> columnDataList = new ArrayList<String>();
        Class tempClass = o.getClass();
        Map<String, Class> idAnnotation = new HashMap();
        Map<String, Class> mysqlIdAnnotation = new HashMap();
        while (tempClass != null && !"java.lang.object".equals(tempClass.getName().toLowerCase())) {// 当父类为null的时候说明到达了最上层的父类(Object类).
            Field[] fd = tempClass.getDeclaredFields();
            allFields.addAll(Arrays.asList(fd));
            tempClass = tempClass.getSuperclass(); // 得到父类,然后赋给自己
            for (Field field : fd) {
                Annotation an = field.getAnnotation(Id.class);
                Annotation mysqlAnnotation = field.getAnnotation(MysqlAutoPK.class);
                if (an != null) {
                    GeneratedValue gv = field.getAnnotation(GeneratedValue.class);
                    if (gv == null || !gv.strategy().equals(GenerationType.IDENTITY)) {
                        idAnnotation.put(field.getName(), Id.class);
                    }
                }
                if (mysqlAnnotation != null) {
                    mysqlIdAnnotation.put(field.getName(), MysqlAutoPK.class);
                }
            }
        }
        for (Field field : allFields) {
            // 如果含有javax.persistence.Transient @Transient ，该字段将不做为持久化数据库操作
            if (field.isAnnotationPresent(Transient.class)) {
                continue;
            }
            String fieldName=field.getName();
            if (mysqlIdAnnotation.get(fieldName) != null) {
                columnAttrList.add(fieldName);
                columnAttrAnnotationMap.put(fieldName, MysqlAutoPK.class);
                fieldName=field.isAnnotationPresent(Column.class)?field.getAnnotation(Column.class).name():fieldName;
                columnDataList.add(fieldName);
            } else if (idAnnotation.get(fieldName) != null) {
                columnAttrList.add(fieldName);
                columnAttrAnnotationMap.put(fieldName, Id.class);
                fieldName=field.isAnnotationPresent(Column.class)?field.getAnnotation(Column.class).name():fieldName;
                columnDataList.add(fieldName);
            } else if (field.isAnnotationPresent(Column.class)) {
                columnAttrList.add(fieldName);
                columnAttrAnnotationMap.put(fieldName, Column.class);
                fieldName=field.isAnnotationPresent(Column.class)?field.getAnnotation(Column.class).name():fieldName;
                columnDataList.add(fieldName);
            } else {
                // 下面控制实体所有属性的添加
                if (!"serialVersionUID".equals(field.getName())) {
                    columnAttrList.add(fieldName);
                    columnAttrAnnotationMap.put(fieldName, Column.class);
                    fieldName=field.isAnnotationPresent(Column.class)?field.getAnnotation(Column.class).name():fieldName;
                    columnDataList.add(fieldName);
                }
            }
        }
        columnMap.put(o.getClass().getName(), columnAttrList);
        columnMap.put(o.getClass().getSimpleName(), columnDataList);
    }

    /**
     * 用于获取数据表Insert的字段累加
     *
     * @return
     */
    public String returnInsertTableColumnsName(Object o) {
        StringBuilder sb = new StringBuilder();
        List<String> columnAttrList = columnMap.get(o.getClass().getName());
        List<String> columnDataList = columnMap.get(o.getClass().getSimpleName());
        int i = 0;
        for (int x=0;x<columnAttrList.size();x++) {
            String attrColumn=columnAttrList.get(x);
            // 增加mysql自增整型主键的处理
            if (columnAttrAnnotationMap.get(attrColumn).getName().equals(MysqlAutoPK.class.getName()) && isNull(o, attrColumn)) {
                continue;
            } else if (columnAttrAnnotationMap.get(attrColumn).getName().equals(Id.class.getName())) {
                if (isNull(o, attrColumn)) {
                    throw new PojoStructureException("undefine POJO PRIMARY @Id value, need PRIMARY id  number value and not null");
                }
            } else if (isNull(o, attrColumn)) {
                continue;
            }
            if (i++ != 0) {
                sb.append(',');
            }
            sb.append(columnDataList.get(x));
        }
        return sb.toString();
    }

    /**
     * 用于获取实体属性Insert的字段映射累加
     *
     * @return
     */
    public String returnInsertAttrColumnsName(Object o) {
        StringBuilder sb = new StringBuilder();
        List<String> columnAttrList = columnMap.get(o.getClass().getName());
        int i = 0;
        for (int x=0;x<columnAttrList.size();x++) {
            String attrColumn=columnAttrList.get(x);
            if (columnAttrAnnotationMap.get(attrColumn).getName().equals(MysqlAutoPK.class.getName()) && isNull(o, attrColumn)) {
                continue;
            } else if (columnAttrAnnotationMap.get(attrColumn).getName().equals(Id.class.getName())) {
                if (isNull(o, attrColumn)) {
                    throw new PojoStructureException("undefine POJO PRIMARY @Id value, need PRIMARY id  number value and not null");
                }
            } else if (isNull(o, attrColumn)) {
                continue;
            }
            if (i++ != 0) {
                sb.append(',');
            }
            sb.append("#{").append("pojo." + attrColumn).append('}');
        }
        return sb.toString();
    }

    /**
    * @Description 根据Map参数数据，返回“，”分隔的key字段字符串
    * @Author jiaojing
    * @Date 2021/4/19 10:51
    * @param map
    * @return java.lang.String
    */
    public String returnInsertTableColumnsNameByMap(Map<String, Object>  map) {
        StringBuilder sb = new StringBuilder();
        int i = 0;
        for(Map.Entry<String, Object> entry:map.entrySet()){
            String key=entry.getKey();
            if (i!= 0) {
                sb.append(',');
            }
            sb.append(key);
            i++;
        }
        return sb.toString();
    }

    public String returnInsertAttrColumnsNameMap(Map<String,Object> map) {
        StringBuilder sb = new StringBuilder();
        int i = 0;
        for(Map.Entry<String, Object> entry:map.entrySet()){
            String key=entry.getKey();
            Object value=entry.getValue();
            if (i!= 0) {
                sb.append(',');
            }
            sb.append("#{").append("pojo." + key).append('}');
            i++;
        }
        return sb.toString();
    }
    public String returnUpdateSetMap(Map<String,Object>  map) {
        StringBuilder sb = new StringBuilder();
        int i = 0;
        for(Map.Entry<String, Object> entry:map.entrySet()){
            String key=entry.getKey();
            if(key.equals("id")){
                continue;
            }
            Object value=entry.getValue();
            if (i!= 0) {
                sb.append(',');
            }
            sb.append(key).append("=#{pojo.").append(key).append('}');
            i++;
        }
        return sb.toString();
    }
    /**
    * @Description Map字段排序（根据key排序）
    * @Author jiaojing
    * @Date 2021/4/19 10:49
    * @param map
    * @param sort
    * @return java.util.Map<java.lang.String,java.lang.Object>
    */
    public   Map<String,Object>  getSortMapByKey(Map<String,Object> map,final boolean sort){
        Map<String, Object> treeMap = new TreeMap<String, Object>(
                new Comparator<String>() {
                    public int compare(String obj1, String obj2) {
                        // 降序排序
                        if(sort){
                            return obj1.compareTo(obj2);
                        }else{
                            return obj2.compareTo(obj1);
                        }
                    }
                });
        for(Map.Entry<String, Object> entry:map.entrySet()){
            String key=entry.getKey();
            Object value=entry.getValue();
            treeMap.put(key, value);
        }
        return treeMap;
    }

    /**
     * 用于数据表查询时返回的字段如：id,name,age
     *
     * @param 设定
     * @return
     * @Description:
     * @author 作者 jacking
     * @date 2017-6-16 下午4:08:31
     */
    public String returnColumnsDefine(Object o) {
        StringBuilder sb = new StringBuilder();
        List<String> columnDataList = columnMap.get(o.getClass().getSimpleName());
        int i = 0;
        for (String column : columnDataList) {
            if (i++ != 0) {
                sb.append(',' + column);
            } else {
                sb.append(column);
            }
        }
        return sb.toString();
    }

    /**
     * 用于获取Update Set的字段累加
     *
     * @return
     */
    public String returnUpdateSet(Object o) {
        StringBuilder sb = new StringBuilder();
        List<String> columnAttrList = columnMap.get(o.getClass().getName());
        List<String> columnDataList = columnMap.get(o.getClass().getSimpleName());
        int i = 0;
        Map map = getAllAttrField(o);
        Field objIdmysql=(Field)map.get("objIdmysql");
        Field objId=(Field)map.get("objId");
        for (int x=0;x<columnAttrList.size();x++) {
            String attrColumn=columnAttrList.get(x);
            if (objIdmysql != null && objIdmysql.getName().equals(attrColumn)) {
                continue;
            } else if (objId!= null && objId.getName().equals(attrColumn)) {
                continue;
            } else if (isNull(o, attrColumn)) {
                continue;
            }
            if (i++ != 0) {
                sb.append(',');
            }
            sb.append(columnDataList.get(x)).append("=#{pojo.").append(attrColumn).append('}');
        }
        return sb.toString();
    }

    /**
     * 用于获取Update Set的字段累加
     *以实体属性值为准，null更新为null
     * @return
     */
    public String returnUpdateSetNew(Object o) {
        StringBuilder sb = new StringBuilder();
        List<String> columnAttrList = columnMap.get(o.getClass().getName());
        List<String> columnDataList = columnMap.get(o.getClass().getSimpleName());
        int i = 0;
        Map map = getAllAttrField(o);
        Field objIdmysql=(Field)map.get("objIdmysql");
        Field objId=(Field)map.get("objId");
        for (int x=0;x<columnAttrList.size();x++) {
            String attrColumn=columnAttrList.get(x);
            if (objIdmysql != null && objIdmysql.getName().equals(attrColumn)) {
                continue;
            } else if (objId!= null && objId.getName().equals(attrColumn)) {
                continue;
            }
            if (i++ != 0) {
                sb.append(',');
            }
            sb.append(columnDataList.get(x)).append("=#{pojo.").append(attrColumn).append('}');
        }
        return sb.toString();
    }

    /**
     * 打印类字段信息
     */
    @Override
    public String toString() {
        Field[] fields = o.getClass().getDeclaredFields();
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (Field f : fields) {
            if (Modifier.isStatic(f.getModifiers()) || Modifier.isFinal(f.getModifiers())) {
                continue;
            }
            Object value = null;
            try {
                f.setAccessible(true);
                value = f.get(this);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if (value != null) {
                sb.append(f.getName()).append('=').append(value).append(',');
            }
        }
        sb.append(']');
        return sb.toString();
    }

    /**
     * 获取实体所有属性字段信息
     * @param o
     * @return
     */
    public static Map getAllAttrField(Object o) {
        Map dataMap = new HashMap();
        Class tempClass = o.getClass();
        List<Field> fields = new ArrayList<>();
        Map<String,Field> fieldsMap = new HashMap<>();
        Field objId = null;
        Field objIdmysql = null;
        while (tempClass != null && !"java.lang.object".equals(tempClass.getName().toLowerCase())) {// 当父类为null的时候说明到达了最上层的父类(Object类).
            Field[] fd = tempClass.getDeclaredFields();
            fields.addAll(Arrays.asList(fd));
            tempClass = tempClass.getSuperclass(); // 得到父类,然后赋给自己
            for (Field field : fd) {
                Annotation an = field.getAnnotation(Id.class);
                Annotation mysqlautoid = field.getAnnotation(MysqlAutoPK.class);
                if (an != null) {
                    GeneratedValue gv = field.getAnnotation(GeneratedValue.class);
                    if (gv == null || !gv.strategy().equals(GenerationType.IDENTITY)) {
                    objId = field;
                    }
                }
                if (mysqlautoid != null) {
                  objIdmysql = field;
                }
            }
        }

        for(Field f:fields){
            fieldsMap.put(f.getName(),f);
        }
        dataMap.put("fieldsMap", fieldsMap);
        dataMap.put("fields", fields);
        dataMap.put("objId", objId);
        dataMap.put("objIdmysql", objIdmysql);
        return dataMap;
    }

    /**
     * 以下为一些分页信息,如不需要可以删除
     */
    private int page;
    private int pageSize;

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public static Map<String, List<String>> getColumnMap() {
        return columnMap;
    }

    public static void setColumnMap(Map<String, List<String>> columnMap) {
        MyBatisReflectUtil.columnMap = columnMap;
    }

    public static Map<String, Class<?>> getColumnAttrAnnotationMap() {
        return columnAttrAnnotationMap;
    }

    public static void setColumnAttrAnnotationMap(Map<String, Class<?>> columnAttrAnnotationMap) {
        MyBatisReflectUtil.columnAttrAnnotationMap = columnAttrAnnotationMap;
    }

    public static Object getO() {
        return o;
    }

    public static void setO(Object o) {
        MyBatisReflectUtil.o = o;
    }
}